%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "parse_tree.h"

extern int yylex();
extern int yyparse();
extern int yyrestart();
extern FILE * yyin;

extern int lines;
extern char *yytext;
extern int pos_end;
extern int pos_start;

parse_tree *gt;

void yyerror(const char *s);
parse_tree_node *node(const char *node_name, int children_num, ...);
%}

%union {
    struct _parse_tree_node *node;
}

// 运算符优先级声明
%left OR
%left AND
%left EQ NEQ
%left LT LET GT GET
%left ADD SUB
%left MUL DIV MOD
%right NOT
%right '='
%nonassoc IFX
%nonassoc ELSE

// 所有 token 声明为 <node> 类型
%token <node> IDENT
%token <node> INTCONST
%token <node> FLOATCONST
%token <node> INT
%token <node> FLOAT
%token <node> VOID
%token <node> SEMICOLON
%token <node> COMMA
%token <node> ASSIGN
%token <node> LT
%token <node> LET
%token <node> GT
%token <node> GET
%token <node> EQ
%token <node> NEQ
%token <node> ADD
%token <node> SUB
%token <node> MUL
%token <node> DIV
%token <node> MOD
%token <node> AND
%token <node> OR
%token <node> NOT
%token <node> LPARENTHESIS
%token <node> RPARENTHESIS
%token <node> LBRACKET
%token <node> RBRACKET
%token <node> LBRACE
%token <node> RBRACE
%token <node> RETURN
%token <node> IF
%token <node> ELSE
%token <node> WHILE
%token <node> BREAK
%token <node> CONTINUE
%token <node> CONST

%type <node> Program
%type <node> CompUnit
%type <node> Decl
%type <node> ConstDecl
%type <node> ConstDefList
%type <node> BType
%type <node> ConstDef
%type <node> ConstExpList
%type <node> ConstInitVal
%type <node> ConstInitValList
%type <node> VarDecl
%type <node> VarDeclList
%type <node> VarDef
%type <node> InitVal
%type <node> InitValList
%type <node> FuncDef
%type <node> FuncFParams
%type <node> FuncFParam
%type <node> ExpList
%type <node> Block
%type <node> BlockList
%type <node> BlockItem
%type <node> Stmt
%type <node> Exp
%type <node> Cond
%type <node> LVal
%type <node> PrimaryExp
%type <node> Number
%type <node> UnaryExp
%type <node> UnaryOp
%type <node> FuncRParams
%type <node> MulExp
%type <node> AddExp
%type <node> RelExp
%type <node> EqExp
%type <node> LAndExp
%type <node> LOrExp
%type <node> ConstExp
%type <node> Integer
%type <node> Floatnum

%start Program

%%
Program : CompUnit { 
    $$ = node("Program", 1, $1); 
    gt->root = $$;
};

CompUnit : CompUnit Decl         { $$ = node("CompUnit", 2, $1, $2); }
         | CompUnit FuncDef      { $$ = node("CompUnit", 2, $1, $2); }
         | Decl                  { $$ = node("CompUnit", 1, $1); }
         | FuncDef               { $$ = node("CompUnit", 1, $1); }
;

Decl : ConstDecl                 { $$ = node("Decl", 1, $1); }
     | VarDecl                   { $$ = node("Decl", 1, $1); }
;

ConstDecl : CONST BType ConstDefList SEMICOLON { 
    $$ = node("ConstDecl", 4, $1, $2, $3, $4); 
};

ConstDefList : ConstDef           { $$ = node("ConstDefList", 1, $1); }
             | ConstDefList COMMA ConstDef { 
                 $$ = node("ConstDefList", 3, $1, $2, $3); 
               }
;

BType : INT        { $$ = node("BType", 1, $1); }
      | FLOAT      { $$
 = node("BType", 1, $1); }
;

ConstDef : IDENT ConstExpList ASSIGN ConstInitVal { 
    $$ = node("ConstDef", 4, $1, $2, $3, $4); 
};

ConstExpList : /* epsilon */      { $$
 = node("ConstExpList", 0); }
             | ConstExpList LBRACKET ConstExp RBRACKET { 
                 $$ = node("ConstExpList", 4, $1, $2, $3, $4); 
               }
;

ConstInitVal : ConstExp           { $$
 = node("ConstInitVal", 1, $1); }
             | LBRACE ConstInitValList RBRACE { 
                 $$ = node("ConstInitVal", 3, $1, $2, $3); 
               }
             | LBRACE RBRACE      { $$ = node("ConstInitVal", 2, $1, $2); }
;

ConstInitValList : ConstInitVal               { $$ = node("ConstInitValList", 1, $1); }
                 | ConstInitValList COMMA ConstInitVal { 
                     $$
 = node("ConstInitValList", 3, $1, $2, $3); 
                   }
;

VarDecl : BType VarDef VarDeclList SEMICOLON { 
    $$ = node("VarDecl", 4, $1, $2, $3, $4); 
};

VarDeclList : /* epsilon */       { $$
 = node("VarDeclList", 0); }
            | VarDeclList COMMA VarDef { 
                $$ = node("VarDeclList", 3, $1, $2, $3); 
              }
;

VarDef : IDENT ConstExpList       { $$
 = node("VarDef", 2, $1, $2); }
       | IDENT ConstExpList ASSIGN InitVal { 
           $$ = node("VarDef", 4, $1, $2, $3, $4); 
         }
;

InitVal : Exp                     { $$ = node("InitVal", 1, $1); }
        | LBRACE InitValList RBRACE { $$ = node("InitVal", 3, $1, $2, $3); }
        | LBRACE RBRACE          { $$ = node("InitVal", 2, $1, $2); }
;

InitValList : InitVal             { $$ = node("InitValList", 1, $1); }
            | InitValList COMMA InitVal { 
                $$
 = node("InitValList", 3, $1, $2, $3); 
              }
;

FuncDef : BType IDENT LPARENTHESIS RPARENTHESIS Block { 
           $$ = node("FuncDef", 5, $1, $2, $3, $4, $5); 
         }
         | VOID IDENT LPARENTHESIS RPARENTHESIS Block { 
             $$
 = node("FuncDef", 5, $1, $2, $3, $4, $5); 
           }
         | VOID IDENT LPARENTHESIS FuncFParams RPARENTHESIS Block { 
             $$ = node("FuncDef", 6, $1, $2, $3, $4, $5, $6); 
           }
         | BType IDENT LPARENTHESIS FuncFParams RPARENTHESIS Block { 
             $$ = node("FuncDef", 6, $1, $2, $3, $4, $5, $6); 
           }
;

FuncFParams : FuncFParam          { $$ = node("FuncFParams", 1, $1); }
            | FuncFParams COMMA FuncFParam { 
                $$ = node("FuncFParams", 3, $1, $2, $3); 
              }
;

FuncFParam : BType IDENT          { $$ = node("FuncFParam", 2, $1, $2); }
           | BType IDENT LBRACKET RBRACKET ExpList { 
               $$ = node("FuncFParam", 5, $1, $2, $3, $4, $5); 
             }
;

ExpList : /* epsilon */           { $$ = node("ExpList", 0); }
        | ExpList LBRACKET Exp RBRACKET { 
            $$ = node("ExpList", 4, $1, $2, $3, $4); 
          }
;

Block : LBRACE BlockList RBRACE   { $$ = node("Block", 3, $1, $2, $3); }
;

BlockList : /* epsilon */        { $$ = node("BlockList", 0); }
          | BlockList BlockItem  { $$ = node("BlockList", 2, $1, $2); }
;

BlockItem : Decl                 { $$ = node("BlockItem", 1, $1); }
          | Stmt                 { $$ = node("BlockItem", 1, $1); }
;

Stmt : LVal ASSIGN Exp SEMICOLON { $$ = node("Stmt", 4, $1, $2, $3, $4); }
     | Exp SEMICOLON             { $$ = node("Stmt", 2, $1, $2); }
     | SEMICOLON                 { $$ = node("Stmt", 1, $1); }
     | Block                     { $$
 = node("Stmt", 1, $1); }
     | IF LPARENTHESIS Cond RPARENTHESIS Stmt{ 
         $$ = node("Stmt", 5, $1, $2, $3, $4, $5); 
       }
     | IF LPARENTHESIS Cond RPARENTHESIS Stmt ELSE Stmt { 
         $$
 = node("Stmt", 7, $1, $2, $3, $4, $5, $6, $7); 
       }
     | WHILE LPARENTHESIS Cond RPARENTHESIS Stmt { 
         $$ = node("Stmt", 5, $1, $2, $3, $4, $5); 
       }
     | BREAK SEMICOLON           { $$ = node("Stmt", 2, $1, $2); }
     | CONTINUE SEMICOLON        { $$ = node("Stmt", 2, $1, $2); }
     | RETURN Exp SEMICOLON      { $$ = node("Stmt", 3, $1, $2, $3); }
     | RETURN SEMICOLON          { $$ = node("Stmt", 2, $1, $2); }
;

Exp : AddExp                     { $$ = node("Exp", 1, $1); }
;

Cond : LOrExp                    { $$ = node("Cond", 1, $1); }
;

LVal : IDENT ExpList             { $$ = node("LVal", 2, $1, $2); }
;

PrimaryExp : LPARENTHESIS Exp RPARENTHESIS { $$ = node("PrimaryExp", 3, $1, $2, $3); }
           | LVal                 { $$ = node("PrimaryExp", 1, $1); }
           | Number               { $$ = node("PrimaryExp", 1, $1); }
;

Number : Integer                 { $$ = node("Number", 1, $1); }
       | Floatnum                { $$ = node("Number", 1, $1); }
;

UnaryExp : PrimaryExp            { $$
 = node("UnaryExp", 1, $1); }
         | IDENT LPARENTHESIS FuncRParams RPARENTHESIS { 
             $$ = node("UnaryExp", 4, $1, $2, $3, $4); 
           }
         | IDENT LPARENTHESIS RPARENTHESIS { 
             $$ = node("UnaryExp", 3, $1, $2, $3); 
           }
         | UnaryOp UnaryExp       { $$ = node("UnaryExp", 2, $1, $2); }
;

UnaryOp : ADD                    { $$ = node("UnaryOp", 1, $1); }
        | SUB                    { $$ = node("UnaryOp", 1, $1); }
        | NOT                    { $$ = node("UnaryOp", 1, $1); }
;

FuncRParams : Exp                 { $$ = node("FuncRParams", 1, $1); }
            | FuncRParams COMMA Exp { $$ = node("FuncRParams", 3, $1, $2, $3); }
;

MulExp : UnaryExp                { $$ = node("MulExp", 1, $1); }
       | MulExp MUL UnaryExp     { $$ = node("MulExp", 3, $1, $2, $3); }
       | MulExp DIV UnaryExp     { $$ = node("MulExp", 3, $1, $2, $3); }
       | MulExp MOD UnaryExp     { $$ = node("MulExp", 3, $1, $2, $3); }
;

AddExp : MulExp                  { $$ = node("AddExp", 1, $1); }
       | AddExp ADD MulExp       { $$ = node("AddExp", 3, $1, $2, $3); }
       | AddExp SUB MulExp       { $$ = node("AddExp", 3, $1, $2, $3); }
;

RelExp : AddExp                  { $$ = node("RelExp", 1, $1); }
       | RelExp LT AddExp        { $$ = node("RelExp", 3, $1, $2, $3); }
       | RelExp LET AddExp       { $$ = node("RelExp", 3, $1, $2, $3); }
       | RelExp GT AddExp        { $$ = node("RelExp", 3, $1, $2, $3); }
       | RelExp GET AddExp       { $$ = node("RelExp", 3, $1, $2, $3); }
;

EqExp : RelExp                   { $$ = node("EqExp", 1, $1); }
      | EqExp EQ RelExp          { $$ = node("EqExp", 3, $1, $2, $3); }
      | EqExp NEQ RelExp         { $$ = node("EqExp", 3, $1, $2, $3); }
;

LAndExp : EqExp                  { $$ = node("LAndExp", 1, $1); }
        | LAndExp AND EqExp      { $$ = node("LAndExp", 3, $1, $2, $3); }
;

LOrExp : LAndExp                 { $$ = node("LOrExp", 1, $1); }
       | LOrExp OR LAndExp       { $$ = node("LOrExp", 3, $1, $2, $3); }
;

ConstExp : AddExp                { $$ = node("ConstExp", 1, $1); }
;

Integer : INTCONST               { $$ = node("Integer", 1, $1); }
;

Floatnum : FLOATCONST            { $$ = node("Floatnum", 1, $1); }
;
%%

void yyerror(const char *s)
{
    fprintf(stderr, "Error at line %d: %s\n", lines, s);
    exit(1);
}

parse_tree *parse(const char *input_path)
{
    if (input_path != NULL) {
        if (!(yyin = fopen(input_path, "r"))) {
            fprintf(stderr, "[ERR] Open input file %s failed.\n", input_path);
            exit(1);
        }
    } else {
        yyin = stdin;
    }
    lines = pos_start = pos_end = 1;
    gt = new_parse_tree();
    yyrestart(yyin);
    yyparse();
    return gt;
}

parse_tree_node *node(const char *name, int children_num, ...)
{
    parse_tree_node *p = new_parse_tree_node(name);
    va_list ap;
    va_start(ap, children_num);
    for (int i = 0; i < children_num; ++i) {
        parse_tree_node *child = va_arg(ap, parse_tree_node *);
        parse_tree_add_child(p, child);
    }
    va_end(ap);
    return p;
} 
